/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       map_header.cpp

	$Header: /game/map_header.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "map_header.h"

#include <algorithm>
#include <limits>
#include <stdlib.h>

#include "bitset_io.h"
#include "difficulty_level.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "progress_handler.h"
#include "streambuf_operators.h"

extern t_external_string const k_default_loss_condition("default_loss_condition");
extern t_external_string const k_default_victory_condition("default_victory_condition");

// ------------------------------------------------
// read an adventure map header
// ------------------------------------------------
bool read(
	std::streambuf &		buffer,
	t_map_header &			header,
	t_progress_handler *	handler )
{
	header.version = get< t_uint16 >( buffer );
	if ( header.version < 20 || header.version > 27 )
		return false;

	header.size = get<t_uint16>( buffer );
	header.levels = get<t_uint8>(buffer );
	header.random_seed = get<t_int32>( buffer );

	std::fill_n( header.playable, ELEMENTS_OF( header.playable ), false );
	std::fill_n( header.can_be_human, ELEMENTS_OF( header.can_be_human ), false );

	header.players = get<t_uint8>( buffer );
	header.humans = 0;
	if ( header.players <= 0 || header.players > k_active_player_color_count )
		return false;

	bool computer_player = false;
	int  i;

	for (i = 0; i < header.players; i++)
	{
		t_player_color color = t_player_color( get< t_uint8 >( buffer ) );

		if ( color < 0 || color >= k_active_player_color_count )
			return false;

		header.playable[color] = true;

		header.can_be_human[color] = ( get< t_uint8 >( buffer ) != 0 );

		// Discard customized alignment flag
		get< t_uint8 >( buffer );

		header.available_alignments[color].read( buffer );

		if ( header.can_be_human[color]  )
			header.humans++;
		else
			computer_player = true;
	}

	if ( header.humans == 0 )
		return false;

	if ( !::read_string16( buffer, header.name ) )
		return false;

	header.difficulty = t_difficulty( get< t_uint8 >( buffer ) );
	if ( header.difficulty < k_difficulty_easy || header.difficulty >= k_difficulty_count )
		return false;

	if ( !::read_string16( buffer, header.description ) ) 
		return false;


	// Read team info
	t_player_color color;
	for ( color = t_player_color(0); color < k_active_player_color_count; enum_incr( color ) )
	{
		header.team_id[ color ] = std::numeric_limits< t_uint8 >::max();
	}

	header.human_teams = 0;

	int team_version = get< t_uint16 >( buffer );

	if ( team_version > 0 )
		return false;

	t_uint8 num_teams = get< t_uint8 >( buffer );
	t_uint8 team = 0;

	while ( team < num_teams )
	{
		bool has_human = false;

		std::bitset< k_active_player_color_count > team_colors = get_bitset< k_active_player_color_count >( buffer );

		for ( color = t_player_color(0); color < k_active_player_color_count; enum_incr( color ) )
		{
			if ( team_colors[ color ] )
			{
				if ( header.team_id[ color ] != std::numeric_limits< t_uint8 >::max() )
				{
					// Player on two teams
					return false;
				}

				header.team_id[ color ] = team;

				if ( header.can_be_human[ color ] )
				{
					has_human = true;
				}
			}
		}

		if ( has_human )
			header.human_teams++;

		team++;
	}

	// Check teams for correctness
	for ( color = t_player_color(0); color < k_active_player_color_count; enum_incr( color ) )
	{
		if ( header.playable[ color ] )
		{
			if ( header.team_id[ color ] == std::numeric_limits< t_uint8 >::max() )
			{
				return false;
			}
		}
		else
		{
			if ( header.team_id[ color ] != std::numeric_limits< t_uint8 >::max() )
			{
				return false;
			}
		}
	}

	bool has_custom_loss_condition = ( get< t_uint8 >( buffer ) != 0 );
	if ( has_custom_loss_condition )
	{
		if ( !read_string16( buffer, header.loss_condition ) )
			return false;
	}
	else
	{
		header.loss_condition = k_default_loss_condition;
	}

	bool has_custom_victory_condition = ( get< t_uint8 >( buffer ) != 0 );
	if ( has_custom_victory_condition )
	{
		if ( !read_string16( buffer, header.victory_condition ) )
			return false;
	}
	else
	{
		header.victory_condition = k_default_victory_condition;
	}

	if ( header.version >= 25 )
		header.enable_standard_victory = get< t_int8 >( buffer ) != 0;
	else
		header.enable_standard_victory = true;

	if ( header.version >= 24 )
	{
		if ( get< t_uint8 >( buffer ) != 0 && !header.prologue_info.read_from_map( buffer ) )
			return false;
		if ( get< t_uint8 >( buffer ) != 0 && !header.epilogue_info.read_from_map( buffer ) )
			return false;
	}
	else
	{
		header.prologue_info = t_scenario_cut_scene_info();
		header.epilogue_info = t_scenario_cut_scene_info();
	}

	if ( header.version >= 27 )
	{
		if ( !read_string16( buffer, header.carryover_description ) )
			return false;
	}
	else
		header.carryover_description = std::string();

	if (handler != 0)
		handler->increment( 12 );

	return true;
}
